package com.kinghood.productcenter.service.async.impl;

import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.kinghood.infrastructure.model.constant.CommonConstants;
import com.kinghood.infrastructure.util.base.IdGenUtil;
import com.kinghood.productcenter.common.model.dao.base.BaseBrandDO;
import com.kinghood.productcenter.common.model.dao.base.BaseCategoryDO;
import com.kinghood.productcenter.common.model.dao.base.BaseGoodsDO;
import com.kinghood.productcenter.common.model.dao.base.BaseSupplierDO;
import com.kinghood.productcenter.common.model.dao.product.BusinessLogDO;
import com.kinghood.productcenter.common.model.dao.product.ProductAttributeDO;
import com.kinghood.productcenter.common.model.dao.product.ProductBaseDO;
import com.kinghood.productcenter.common.model.dto.product.*;
import com.kinghood.productcenter.common.model.dto.sku.ProductSkuDTO;
import com.kinghood.productcenter.common.model.dto.sku.ProductSkuReqDTO;
import com.kinghood.productcenter.common.model.enums.product.BusinessLogOperateEnum;
import com.kinghood.productcenter.dao.base.*;
import com.kinghood.productcenter.service.async.IAsyncBusinessLogService;
import com.kinghood.productcenter.service.async.handler.BusinessLogHandler;
import com.kinghood.productcenter.service.base.IBaseSupplierService;
import com.kinghood.productcenter.service.product.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

/**
 * description
 *
 * @author zhangFanJun
 * @date 2024-01-08 14:54
 **/
@RequiredArgsConstructor
@Slf4j
@Transactional(rollbackFor = Exception.class)
@Service
public class AsyncBusinessLogServiceImpl implements IAsyncBusinessLogService {

    private final IBusinessLogService businessLogService;
    private final IProductAttributeService productAttributeService;
    private final BaseBrandMapper brandMapper;
    private final BaseGoodsMapper goodsMapper;
    private final BaseCategoryMapper categoryMapper;
    private final BaseSupplierMapper supplierMapper;
    @Lazy
    @Resource
    private IProductBaseService productBaseService;

    @Async("taskExecutor")
    @Override
    public void addLogBusinessIdBatch(List<Long> productIdList, String content, Integer operate, Long userId) {

        if(CollectionUtils.isEmpty(productIdList)){
            return;
        }
        ArrayList<BusinessLogDO> logs = new ArrayList<>();
        productIdList.forEach(x->{
            BusinessLogDO log = new BusinessLogDO();
            log.setId(IdGenUtil.getId());
            log.setProductId(x);
            log.setContent(content);
            log.setOperate(operate);
            log.setCreateTime(LocalDateTime.now());
            log.setCreateUserId(userId);
            logs.add(log);
        });
        businessLogService.saveBatch(logs);
    }

    @Async("taskExecutor")
    @Override
    public void addLogBatch(List<BusinessLogDO> list) {

        if(CollectionUtils.isEmpty(list)){
            return;
        }
        businessLogService.saveBatch(list);
    }

    @Async("taskExecutor")
    @Override
    public void addLog(Long skuId, Long productId, String content, Integer operate, Long userId) {

        addLog(skuId,productId,content,operate,userId,LocalDateTime.now());
    }

    @Async("taskExecutor")
    @Override
    public void addLog(Long skuId, Long productId, String content, Integer operate, Long userId,LocalDateTime dateTime) {

        BusinessLogDO log = new BusinessLogDO();
        log.setId(IdGenUtil.getId());
        log.setProductId(productId);
        log.setSkuId(skuId);
        log.setContent(content);
        log.setOperate(operate);
        log.setCreateTime(dateTime);
        log.setCreateUserId(userId);
        businessLogService.save(log);
    }

    @Async("taskExecutor")
    @Override
    public void addUpdateProductLog(UpdateProductLogDTO in, boolean sku){

        try{
            LocalDateTime now = LocalDateTime.now();
            BaseData baseData = getBaseData(in.getIn().getProductBase().getId());
            if(!sku){
                String productContent = handleUpdateProductLog(in,baseData);
                if(!StringUtils.isEmpty(productContent)){
                    addLog(null, in.getIn().getProductBase().getId(), productContent,
                            BusinessLogOperateEnum.UPDATE.getType(), in.getUserId(),now);
                }
            }

            if(!CollectionUtils.isEmpty(in.getOldSkuCodeMap())){
                List<String> skuCodes = new ArrayList<>();
                if(!CollectionUtils.isEmpty(in.getIn().getSkuList())){
                    skuCodes = in.getIn().getSkuList().stream().map(ProductSkuReqDTO::getSkuCode).collect(Collectors.toList());
                }
                // sku删除
                List<String> finalSkuCodes = skuCodes;
                ProductDTO oldProductDTO = in.getOldProductDTO();
                oldProductDTO.getSkuList().forEach(x->{
                    if(!finalSkuCodes.contains(x.getSkuCode())){
                        StringBuilder sb = new StringBuilder();
                        sb.append("删除sku编码:").append("[").append(x.getSkuCode()).append("]").append(";");
                        // sku
                        BusinessLogHandler.skuAttributeContent(x.getAttributes(),in.getOldSkuAttributeMap(),
                                Collections.emptyMap(),sb, x.getId(), ProductAttributeDO.BUSINESS_TYPE_SKU);
                        // 附件
                        if(!CollectionUtils.isEmpty(oldProductDTO.getSkuExtrasList())){
                            oldProductDTO.getSkuExtrasList().forEach(y->{
                                if(!Objects.equals(x.getSkuCode(),y.getSkuCode())){
                                    return ;
                                }
                                BusinessLogHandler.skuAttributeContent(y.getAttributes(),in.getOldSkuAttributeMap(),
                                        Collections.emptyMap(),sb,y.getId(), ProductAttributeDO.BUSINESS_TYPE_EXTRAS);
                            });
                        }
                        // 供应商
                        if(!CollectionUtils.isEmpty(oldProductDTO.getSkuSupplierList())){
                            oldProductDTO.getSkuSupplierList().forEach(y->{
                                if(!Objects.equals(x.getSkuCode(),y.getSkuCode())){
                                    return ;
                                }
                                BusinessLogHandler.skuAttributeContent(y.getAttributes(),in.getOldSkuAttributeMap(),
                                        Collections.emptyMap(),sb,y.getId(), ProductAttributeDO.BUSINESS_TYPE_EXTRAS);
                            });
                        }
                        addLog(x.getId(), in.getIn().getProductBase().getId(), sb.toString(),
                                BusinessLogOperateEnum.UPDATE.getType(), in.getUserId(),now);
                    }
                });
            }

            // sku新增或者修改
            if(!CollectionUtils.isEmpty(baseData.product.getSkuList())){
                for (ProductSkuDTO productSkuDTO : baseData.product.getSkuList()) {
                    String skuContent = handleUpdateSkuLog(productSkuDTO,in,baseData);
                    if(Objects.isNull(skuContent)){
                        continue;
                    }
                    addLog(productSkuDTO.getId(), in.getIn().getProductBase().getId(), skuContent,
                            BusinessLogOperateEnum.UPDATE.getType(), in.getUserId(),now);
                }
            }
        } catch (Exception e){
            log.error("添加业务日志异常：",e);
        }
    }

    private class BaseData{
        Map<Long, String> brandMap = new HashMap<>();
        Map<Long, String> goodsMap = new HashMap<>();
        Map<Long, String> categoryMap = new HashMap<>();
        Map<Long, String> supplierMap = new HashMap<>();
        ProductDTO product;
        List<AttributeByProductIdDTO> attributeList;
        Map<String, AttributeByProductIdDTO> productAttributeMap = new HashMap<>();
        Map<String, AttributeByProductIdDTO> skuAttributeMap = new HashMap<>();
    }

    /**
     * 封装sku日志
     * */
    private String handleUpdateSkuLog(ProductSkuDTO productSkuDTO, UpdateProductLogDTO in, BaseData baseData) {

        StringBuilder sb0 = new StringBuilder();
        StringBuilder sb = new StringBuilder();
        Set<String> oldKeySet = in.getOldSkuCodeMap().keySet();
        String skuCode = productSkuDTO.getSkuCode();
        if(!oldKeySet.contains(skuCode)){
            sb0.append("新增sku编码:").append("[").append(skuCode).append("]").append(";");
        } else {
            sb0.append("修改sku编码:").append("[").append(skuCode).append("]").append(";");
        }
        Map<String, AttributeByProductIdDTO> skuAttributeMap = baseData.skuAttributeMap;
        Map<String, AttributeByProductIdDTO> oldSkuAttributeMap = in.getOldSkuAttributeMap();
        List<ProductSkuExtrasDTO> skuExtrasList = baseData.product.getSkuExtrasList();
        List<ProductSupplierDTO> skuSupplierList = baseData.product.getSkuSupplierList();
        // sku
        BusinessLogHandler.skuAttributeContent(productSkuDTO.getAttributes(),oldSkuAttributeMap,skuAttributeMap,sb,
                productSkuDTO.getId(), ProductAttributeDO.BUSINESS_TYPE_SKU);
        // 附件
        if(!CollectionUtils.isEmpty(skuExtrasList)){
            skuExtrasList.forEach(x->{
                if(!Objects.equals(skuCode,x.getSkuCode())){
                    return ;
                }
                BusinessLogHandler.skuAttributeContent(x.getAttributes(),oldSkuAttributeMap,skuAttributeMap,sb,x.getId(), ProductAttributeDO.BUSINESS_TYPE_EXTRAS);
            });
        }

        // 供应商
        if(!CollectionUtils.isEmpty(skuSupplierList)){
            skuSupplierList.forEach(x->{
                if(!Objects.equals(skuCode,x.getSkuCode())){
                    return ;
                }
                BusinessLogHandler.skuAttributeContent(x.getAttributes(),oldSkuAttributeMap,skuAttributeMap,sb,x.getId(), ProductAttributeDO.BUSINESS_TYPE_EXTRAS);
            });
        }

        if(sb.length() == 0){
            return null;
        } else {
            return sb0.append(sb).toString();
        }
    }

    public String handleUpdateProductLog(UpdateProductLogDTO param, BaseData baseData) {

        ProductReqDTO in = param.getIn();
        ProductDTO oldProduct = param.getOldProductDTO();
        StringBuilder sb = new StringBuilder();
        if(!Objects.equals(in.getBrandId(),oldProduct.getBrandId())){
            BusinessLogHandler.setContent(sb,"品牌",baseData.brandMap.get(oldProduct.getBrandId()),
                    baseData.brandMap.get(in.getBrandId()));
        }
        if(!Objects.equals(in.getGoodsId(),oldProduct.getGoodsId())){
            BusinessLogHandler.setContent(sb,"货类",baseData.goodsMap.get(oldProduct.getGoodsId()),
                    baseData.goodsMap.get(in.getGoodsId()));
        }
        if(!Objects.equals(in.getCategoryId(),oldProduct.getCategoryId())){
            BusinessLogHandler.setContent(sb,"品类",baseData.categoryMap.get(oldProduct.getCategoryId()),
                    baseData.categoryMap.get(in.getCategoryId()));
        }

        BusinessLogHandler.handleUpdateProductLog(sb,param,baseData.productAttributeMap);

        return sb.toString();
    }

    /**
     *
     * 获取基础数据
     * */
    private BaseData getBaseData(Long productId) {

        BaseData baseData = new BaseData();

        ArrayList<CompletableFuture> list = new ArrayList<>();
        CompletableFuture<Void> f4 = CompletableFuture.runAsync(() -> {
            List<BaseBrandDO> brandList = new LambdaQueryChainWrapper<>(brandMapper)
                    .eq(BaseBrandDO::getIsDelete, CommonConstants.IS_DELETE_N)
                    .eq(BaseBrandDO::getStatus, CommonConstants.ENABLE).list();
            if (!CollectionUtils.isEmpty(brandList)) {
                Map<Long, String> map = brandList.stream()
                        .collect(Collectors.toMap( BaseBrandDO::getId,BaseBrandDO::getBrandName,(k1, k2) -> k2));
                baseData.brandMap.putAll(map);
            }
        });
        list.add(f4);
        CompletableFuture<Void> f5 = CompletableFuture.runAsync(() -> {
            List<BaseGoodsDO> goodsList = goodsMapper.getBottomList();
            if (!CollectionUtils.isEmpty(goodsList)) {
                Map<Long, String> map = goodsList.stream()
                        .collect(Collectors.toMap( BaseGoodsDO::getId, BaseGoodsDO::getGoodsName,(k1, k2) -> k2));
                baseData.goodsMap.putAll(map);
            }
        });
        list.add(f5);
        CompletableFuture<Void> f6 = CompletableFuture.runAsync(() -> {
            List<BaseCategoryDO> categoryList = categoryMapper.getBottomList();
            if (!CollectionUtils.isEmpty(categoryList)) {
                Map<Long, String> map = categoryList.stream()
                        .collect(Collectors.toMap( BaseCategoryDO::getId,BaseCategoryDO::getCategoryName, (k1, k2) -> k2));
                baseData.categoryMap.putAll(map);
            }
        });
        list.add(f6);
        CompletableFuture<Void> f7 = CompletableFuture.runAsync(() -> {
            List<BaseSupplierDO> supplierList = new LambdaQueryChainWrapper<>(supplierMapper)
                    .eq(BaseSupplierDO::getIsDelete, CommonConstants.IS_DELETE_N)
                    .eq(BaseSupplierDO::getStatus, CommonConstants.ENABLE).list();
            if (!CollectionUtils.isEmpty(supplierList)) {
                Map<Long, String> map = supplierList.stream()
                        .collect(Collectors.toMap(BaseSupplierDO::getId,BaseSupplierDO::getSupplierName,  (k1, k2) -> k2));
                baseData.supplierMap.putAll(map);
            }
        });
        list.add(f7);
        CompletableFuture<Void> f8 = CompletableFuture.runAsync(() -> {
            List<AttributeByProductIdDTO> attributeList = productAttributeService.getAttributeByProductId(productId);
            baseData.attributeList = attributeList;
            if (!CollectionUtils.isEmpty(attributeList)) {
                Map<String, AttributeByProductIdDTO> map = attributeList.stream()
                        .filter(x->Objects.equals(x.getBusinessType(),0))
                        .collect(Collectors.toMap(x -> x.getAttributeCode() + x.getBusinessType() + x.getBusinessId(), v -> v, (k1, k2) -> k2));
                baseData.productAttributeMap.putAll(map);

                Map<String, AttributeByProductIdDTO> skuMap = attributeList.stream()
                        .filter(x->!Objects.equals(x.getBusinessType(),0))
                        .collect(Collectors.toMap(x -> x.getAttributeCode() + x.getBusinessType() + x.getBusinessId(), v -> v, (k1, k2) -> k2));
                baseData.skuAttributeMap.putAll(skuMap);
            }
        });
        list.add(f8);
        CompletableFuture<Void> f9 = CompletableFuture.runAsync(() -> {
            ProductDTO product = productBaseService.getProduct(productId);
            baseData.product = product;
        });
        list.add(f9);

        CompletableFuture.allOf(list.toArray(new CompletableFuture[]{})).join();

        return baseData;
    }

}
